#!/bin/bash

# Bootstrap script for setting up remote nodes with SSH key authentication
# and sudoers configuration for the Gollect monitoring system
#
# Usage: ./bootstrap_node.sh <host> <user> <password> [port] [remote_dir] [key_name]
#
# Example: ./bootstrap_node.sh 192.168.1.100 admin mypassword 22 /opt/trode gollect-key

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Function to print colored output
print_status() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

print_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# Function to check if a command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Function to check if SSH key authentication works
check_ssh_key_auth() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -i "$key_path" -p "$port" "$user@$host" "echo 'test'" >/dev/null 2>&1
    return $?
}

# Function to check if sudo access works
check_sudo_access() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -i "$key_path" -p "$port" "$user@$host" "sudo -n echo 'test'" >/dev/null 2>&1
    return $?
}

# Function to check if sudoers configuration exists
check_sudoers_config() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -i "$key_path" -p "$port" "$user@$host" "sudo test -f /etc/sudoers.d/gollect-$user" >/dev/null 2>&1
    return $?
}

# Function to check if deployment directory exists
check_deployment_dir() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    local remote_dir=$5
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -i "$key_path" -p "$port" "$user@$host" "test -d $remote_dir" >/dev/null 2>&1
    return $?
}

# Function to install sshpass if not available
install_sshpass() {
    if ! command_exists sshpass; then
        print_warning "sshpass not found. Attempting to install..."
        
        if command_exists apt-get; then
            # Ubuntu/Debian
            sudo apt-get update && sudo apt-get install -y sshpass
        elif command_exists yum; then
            # CentOS/RHEL
            sudo yum install -y sshpass
        elif command_exists brew; then
            # macOS
            brew install sshpass
        else
            print_error "Could not install sshpass automatically. Please install it manually:"
            print_error "  Ubuntu/Debian: sudo apt-get install sshpass"
            print_error "  CentOS/RHEL: sudo yum install sshpass"
            print_error "  macOS: brew install sshpass"
            exit 1
        fi
    fi
}

# Function to generate SSH key pair
generate_ssh_key() {
    local host=$1
    local key_name=$2
    
    local keys_dir="keys/$host"
    local key_path="$keys_dir/$key_name"
    
    # Check if key already exists
    if [[ -f "$key_path" ]]; then
        print_status "SSH key already exists at: $key_path"
        echo "$key_path"
        return 0
    fi
    
    print_status "Generating SSH key pair..."
    
    # Create keys directory
    mkdir -p "$keys_dir"
    chmod 700 "$keys_dir"
    
    # Generate SSH key
    ssh-keygen -t ed25519 -f "$key_path" -N "" -C "bootstrap-$USER@$host"
    
    print_success "SSH key generated at: $key_path"
    echo "$key_path"
}

# Function to copy public key to remote server
copy_public_key() {
    local host=$1
    local user=$2
    local password=$3
    local port=$4
    local key_path=$5
    
    # Check if SSH key authentication already works
    if check_ssh_key_auth "$host" "$user" "$port" "$key_path"; then
        print_status "SSH key authentication already works, skipping public key copy"
        return 0
    fi
    
    print_status "Copying public key to remote server..."
    
    local public_key
    public_key=$(cat "$key_path.pub")
    
    # Create the command to copy the public key
    local ssh_cmd="mkdir -p ~/.ssh && echo '$public_key' >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
    
    # Use sshpass to execute the command
    sshpass -p "$password" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p "$port" "$user@$host" "$ssh_cmd"
    
    print_success "Public key copied to remote server"
}

# Function to configure sudoers
configure_sudoers() {
    local host=$1
    local user=$2
    local password=$3
    local port=$4
    local remote_dir=$5
    local key_path=$6
    
    # Check if sudoers configuration already exists
    if check_ssh_key_auth "$host" "$user" "$port" "$key_path" && check_sudoers_config "$host" "$user" "$port" "$key_path"; then
        print_status "Sudoers configuration already exists, skipping"
        return 0
    fi
    
    print_status "Configuring sudoers with whitelist..."
    
    # Create sudoers configuration
    local sudoers_config="# Gollect monitoring system sudoers configuration
# This file allows the $user user to run specific commands without password

# Allow systemctl commands for service management
$user ALL=(ALL) NOPASSWD: /bin/systemctl start gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl stop gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl restart gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl status gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl is-active gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl enable gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl disable gollect-trode-*
$user ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload

# Allow process management
$user ALL=(ALL) NOPASSWD: /bin/kill
$user ALL=(ALL) NOPASSWD: /bin/ps
$user ALL=(ALL) NOPASSWD: /usr/bin/pgrep

# Allow file operations in the trode directory
$user ALL=(ALL) NOPASSWD: /bin/chmod +x $remote_dir/*
$user ALL=(ALL) NOPASSWD: /bin/chown $user:$user $remote_dir/*
$user ALL=(ALL) NOPASSWD: /bin/mkdir -p $remote_dir
$user ALL=(ALL) NOPASSWD: /bin/rm -f $remote_dir/*.pid
$user ALL=(ALL) NOPASSWD: /bin/rm -f $remote_dir/*.log

# Allow network operations for monitoring
$user ALL=(ALL) NOPASSWD: /bin/netstat
$user ALL=(ALL) NOPASSWD: /bin/ss
$user ALL=(ALL) NOPASSWD: /usr/bin/lsof

# Allow system information gathering
$user ALL=(ALL) NOPASSWD: /bin/cat /proc/cpuinfo
$user ALL=(ALL) NOPASSWD: /bin/cat /proc/meminfo
$user ALL=(ALL) NOPASSWD: /bin/df
$user ALL=(ALL) NOPASSWD: /bin/free
$user ALL=(ALL) NOPASSWD: /usr/bin/top -b -n 1
$user ALL=(ALL) NOPASSWD: /usr/bin/uptime"

    # Create temporary file
    local temp_file="/tmp/gollect-sudoers-$user"
    echo "$sudoers_config" > "$temp_file"
    
    # Copy to remote server
    sshpass -p "$password" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P "$port" "$temp_file" "$user@$host:/tmp/gollect-sudoers"
    
    # Install sudoers configuration
    local install_cmd="sudo cp /tmp/gollect-sudoers /etc/sudoers.d/gollect-$user && sudo chmod 440 /etc/sudoers.d/gollect-$user && rm /tmp/gollect-sudoers"
    sshpass -p "$password" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p "$port" "$user@$host" "$install_cmd"
    
    # Clean up local temp file
    rm -f "$temp_file"
    
    print_success "Sudoers configuration installed"
}

# Function to test SSH key authentication
test_ssh_key() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    # Check if SSH key authentication already works
    if check_ssh_key_auth "$host" "$user" "$port" "$key_path"; then
        print_status "SSH key authentication already works, skipping test"
        return 0
    fi
    
    print_status "Testing SSH key authentication..."
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i "$key_path" -p "$port" "$user@$host" "echo 'SSH key authentication successful'"
    
    print_success "SSH key authentication test passed"
}

# Function to test sudo access
test_sudo_access() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    # Check if sudo access already works
    if check_sudo_access "$host" "$user" "$port" "$key_path"; then
        print_status "Sudo access already works, skipping test"
        return 0
    fi
    
    print_status "Testing sudo access..."
    
    # Test a simple sudo command
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i "$key_path" -p "$port" "$user@$host" "sudo -n echo 'Sudo access confirmed'"
    
    print_success "Sudo access test passed"
}

# Function to create deployment directory
create_deployment_dir() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    local remote_dir=$5
    
    # Check if deployment directory already exists
    if check_deployment_dir "$host" "$user" "$port" "$key_path" "$remote_dir"; then
        print_status "Deployment directory already exists: $remote_dir"
        return 0
    fi
    
    print_status "Creating deployment directory..."
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i "$key_path" -p "$port" "$user@$host" "sudo mkdir -p $remote_dir && sudo chown $user:$user $remote_dir"
    
    print_success "Deployment directory created: $remote_dir"
}

# Function to rollback public key
rollback_public_key() {
    local host=$1
    local user=$2
    local password=$3
    local port=$4
    local key_path=$5
    
    print_status "Rolling back public key..."
    
    local public_key
    public_key=$(cat "$key_path.pub")
    
    # Remove the public key from authorized_keys
    local ssh_cmd="sed -i '/${public_key//\//\\/}/d' ~/.ssh/authorized_keys"
    
    sshpass -p "$password" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p "$port" "$user@$host" "$ssh_cmd" >/dev/null 2>&1 || true
    
    print_status "Public key rollback completed"
}

# Function to rollback sudoers
rollback_sudoers() {
    local host=$1
    local user=$2
    local port=$3
    local key_path=$4
    
    print_status "Rolling back sudoers configuration..."
    
    ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i "$key_path" -p "$port" "$user@$host" "sudo rm -f /etc/sudoers.d/gollect-$user" >/dev/null 2>&1 || true
    
    print_status "Sudoers rollback completed"
}

# Function to perform complete rollback
perform_rollback() {
    local host=$1
    local user=$2
    local password=$3
    local port=$4
    local key_path=$5
    
    print_warning "Performing bootstrap rollback..."
    
    # Rollback in reverse order
    rollback_sudoers "$host" "$user" "$port" "$key_path"
    rollback_public_key "$host" "$user" "$password" "$port" "$key_path"
    
    # Remove local SSH key
    if [[ -f "$key_path" ]]; then
        rm -f "$key_path" "$key_path.pub"
        print_status "Local SSH key removed"
    fi
    
    print_success "Bootstrap rollback completed"
}

# Main bootstrap function
bootstrap_node() {
    local host=$1
    local user=$2
    local password=$3
    local port=${4:-22}
    local remote_dir=${5:-/opt/trode}
    local key_name=${6:-gollect-key}
    
    print_status "Starting bootstrap process for $user@$host:$port"
    print_status "Remote directory: $remote_dir"
    print_status "SSH key name: $key_name"
    
    # Step 1: Check and install sshpass
    install_sshpass
    
    # Step 2: Generate SSH key
    local key_path
    key_path=$(generate_ssh_key "$host" "$key_name")
    
    # Step 3: Copy public key to remote
    if ! copy_public_key "$host" "$user" "$password" "$port" "$key_path"; then
        print_error "Failed to copy public key"
        perform_rollback "$host" "$user" "$password" "$port" "$key_path"
        exit 1
    fi
    
    # Step 4: Configure sudoers
    if ! configure_sudoers "$host" "$user" "$password" "$port" "$remote_dir" "$key_path"; then
        print_error "Failed to configure sudoers"
        rollback_public_key "$host" "$user" "$password" "$port" "$key_path"
        perform_rollback "$host" "$user" "$password" "$port" "$key_path"
        exit 1
    fi
    
    # Step 5: Test SSH key authentication
    if ! test_ssh_key "$host" "$user" "$port" "$key_path"; then
        print_error "Failed to test SSH key authentication"
        rollback_sudoers "$host" "$user" "$port" "$key_path"
        rollback_public_key "$host" "$user" "$password" "$port" "$key_path"
        perform_rollback "$host" "$user" "$password" "$port" "$key_path"
        exit 1
    fi
    
    # Step 6: Test sudo access
    if ! test_sudo_access "$host" "$user" "$port" "$key_path"; then
        print_error "Failed to test sudo access"
        rollback_sudoers "$host" "$user" "$port" "$key_path"
        perform_rollback "$host" "$user" "$password" "$port" "$key_path"
        exit 1
    fi
    
    # Step 7: Create deployment directory
    if ! create_deployment_dir "$host" "$user" "$port" "$key_path" "$remote_dir"; then
        print_error "Failed to create deployment directory"
        # No rollback needed for directory creation failure
        exit 1
    fi
    
    print_success "Bootstrap completed successfully!"
    print_status "SSH key path: $key_path"
    print_status "You can now use this key for secure SSH access without password"
    print_status "The remote node is ready for trode deployment"
}

# Function to show usage
show_usage() {
    echo "Usage: $0 <host> <user> <password> [port] [remote_dir] [key_name]"
    echo ""
    echo "Arguments:"
    echo "  host        - Remote host IP address or hostname"
    echo "  user        - SSH username"
    echo "  password    - SSH password (will be used only for bootstrap)"
    echo "  port        - SSH port (default: 22)"
    echo "  remote_dir  - Remote directory for trode deployment (default: /opt/trode)"
    echo "  key_name    - Name for the generated SSH key (default: gollect-key)"
    echo ""
    echo "Examples:"
    echo "  $0 192.168.1.100 admin mypassword"
    echo "  $0 server.example.com ubuntu mypass 2222 /opt/gollect my-key"
    echo ""
    echo "This script will:"
    echo "  1. Install sshpass if not available"
    echo "  2. Generate an SSH key pair (if not exists)"
    echo "  3. Copy the public key to the remote server (if not already done)"
    echo "  4. Configure sudoers with a whitelist of allowed commands (if not exists)"
    echo "  5. Test SSH key authentication (if not already working)"
    echo "  6. Test sudo access (if not already working)"
    echo "  7. Create the deployment directory (if not exists)"
    echo ""
    echo "The script is idempotent and can be safely re-run if interrupted."
}

# Check if running as root
if [[ $EUID -eq 0 ]]; then
   print_error "This script should not be run as root"
   exit 1
fi

# Check arguments
if [[ $# -lt 3 ]]; then
    print_error "Insufficient arguments"
    show_usage
    exit 1
fi

# Parse arguments
HOST=$1
USER=$2
PASSWORD=$3
PORT=${4:-22}
REMOTE_DIR=${5:-/opt/trode}
KEY_NAME=${6:-gollect-key}

# Validate host
if [[ -z "$HOST" ]]; then
    print_error "Host cannot be empty"
    exit 1
fi

# Validate user
if [[ -z "$USER" ]]; then
    print_error "User cannot be empty"
    exit 1
fi

# Validate password
if [[ -z "$PASSWORD" ]]; then
    print_error "Password cannot be empty"
    exit 1
fi

# Validate port
if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [[ "$PORT" -lt 1 ]] || [[ "$PORT" -gt 65535 ]]; then
    print_error "Port must be a number between 1 and 65535"
    exit 1
fi

# Run bootstrap
bootstrap_node "$HOST" "$USER" "$PASSWORD" "$PORT" "$REMOTE_DIR" "$KEY_NAME" 